﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using MyBasicLib.Collections.Generic;
using MyBasicLib.Reflection.FastReflection.Emit; 

namespace MyBasicLib.Reflection
{

    internal class MyConstructorInfo : ConstructorInfo
    {
        Type declaringType;
        string name;
        Type reflectedType;
        ParameterInfo[] parameterInfos;
        ConstructorInvoker invoker;
        ConstructorInfo info;
        public MyConstructorInfo(ConstructorInfo info)
            : base()
        {
            this.info = info;
            name = info.Name;
            declaringType = info.DeclaringType;
            reflectedType = info.ReflectedType;
           
        }

        public object FastInvoke(object[] parameters)
        {
            invoker =invoker?? new ConstructorInvoker(info);
            return invoker.Invoke(parameters);
        }

        public override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override MethodAttributes Attributes
        {
            get { throw new NotImplementedException(); }
        }

        public override MethodImplAttributes GetMethodImplementationFlags()
        {
            throw new NotImplementedException();
        }

        public override ParameterInfo[] GetParameters()
        {
            return parameterInfos ?? (parameterInfos = info.GetParameters()) ;
        }

        public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override RuntimeMethodHandle MethodHandle
        {
            get { throw new NotImplementedException(); }
        }



        public override object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            throw new NotImplementedException();
        }

        public override object[] GetCustomAttributes(bool inherit)
        {
            throw new NotImplementedException();
        }

        public override bool IsDefined(Type attributeType, bool inherit)
        {
            throw new NotImplementedException();
        }

        public override Type DeclaringType
        {
            get
            {
                return declaringType;
            }
        }
        public override string Name
        {
            get
            {
                return name;
            }
        }

        public override Type ReflectedType
        {
            get
            {
                return reflectedType;
            }
        }
    }
    internal class MyPropertyInfo : PropertyInfo
    {
        PropertyAccessor invoker;

        Type propertyType;
        string name;
        Type reflectedType;
        object[] customAttributes;
        PropertyInfo info;

        public MyPropertyInfo(PropertyInfo info)
        {
            this.info = info;
            name = info.Name;
            propertyType = info.PropertyType;
            reflectedType = info.ReflectedType;
          
        }

        public object FastGetValue(object parameter)
        {
            invoker = invoker ?? new PropertyAccessor(info);

            return invoker.GetValue(parameter);
        }
        public void FastSetValue(object instance, object value)
        {
            invoker = invoker ?? new PropertyAccessor(info);

            invoker.SetValue(instance, value);

        }

        public override PropertyAttributes Attributes
        {
            get { throw new NotImplementedException(); }
        }

        public override bool CanRead
        {
            get { throw new NotImplementedException(); }
        }

        public override bool CanWrite
        {
            get { throw new NotImplementedException(); }
        }

        public override MethodInfo[] GetAccessors(bool nonPublic)
        {
            throw new NotImplementedException();
        }

        public override MethodInfo GetGetMethod(bool nonPublic)
        {
            throw new NotImplementedException();
        }

        public override ParameterInfo[] GetIndexParameters()
        {
            throw new NotImplementedException();
        }

        public override MethodInfo GetSetMethod(bool nonPublic)
        {
            throw new NotImplementedException();
        }

        public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }


        public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override Type DeclaringType
        {
            get { throw new NotImplementedException(); }
        }

        public override object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            customAttributes = customAttributes??info.GetCustomAttributes(false);
           
            return customAttributes;
        }

        public override object[] GetCustomAttributes(bool inherit)
        {
            throw new NotImplementedException();
        }

        public override bool IsDefined(Type attributeType, bool inherit)
        {
            throw new NotImplementedException();
        }


        public override Type PropertyType
        {
            get { return propertyType; }
        }
        public override string Name
        {
            get
            {
                return name;
            }
        }

        public override Type ReflectedType
        {
            get
            {
                return reflectedType;
            }
        }
    }
    /// <summary>
    /// the TypeData only contains public method,property,field
    /// </summary>
    internal class TypeData
    {
        private object[] _attributeCache;
        private MyConstructorInfo[] _constructorInfoCache;
        private FieldInfo[] _fieldInfoCache;
        private MethodInfo[] _methodInfoCache;
        private Dictionary<String, MyPropertyInfo> _propertyInfoCache;


        private MyConstructorInfo myConstructorInfo;
        private object _propertyInfoCacheLock = new object();
        // private string _propertyNames;
 

        internal TypeData(Type type)
        {
            Type = type;
        }

        public Type Type { get; private set; }

        public MyConstructorInfo[] GetConstructorInfos()
        { 
            
           return _constructorInfoCache ?? (_constructorInfoCache = Type.GetConstructors().Select(p=>new MyConstructorInfo(p)).ToArray());
        }

        public MethodInfo[] GetMethodInfos()
        {
            return _methodInfoCache ?? (_methodInfoCache = Type.GetMethods().OrderBy(p => p.Name).ToArray());
        }

        public Dictionary<String, MyPropertyInfo> GetPropertyInfos()
        {

            if (_propertyInfoCache == null)
            {
                lock (_propertyInfoCacheLock)
                {
                    if (_propertyInfoCache == null)
                    {
                        var ps = Type.GetProperties();
                        _propertyInfoCache = ps.OrderBy(p => p.Name).Select(p => new MyPropertyInfo(p)).ToDictionary<MyPropertyInfo, string>(p => p.Name);
                    }
                }
            }
            return _propertyInfoCache;
        }

        public FieldInfo[] GetFieldInfos()
        {
            return _fieldInfoCache ?? (_fieldInfoCache = Type.GetFields().OrderBy(p => p.Name).ToArray());
        }

        public object[] GetAttributes()
        {
            return _attributeCache ?? (_attributeCache = Type.GetCustomAttributes(false));
        }

        public MyConstructorInfo GetConstructorInfo()
        {
            return myConstructorInfo??(myConstructorInfo=GetConstructorInfos()[0]);
        }
        public MyPropertyInfo GetPropertyInfo(string name)
        {


            GetPropertyInfos();

            MyPropertyInfo pi;
            _propertyInfoCache.TryGetValue(name, out pi);
            //var dfgf = _propertyInfoCache.Keys.ElementAt[0];
            //Array.BinarySearch(null, "");
            return pi;

        }

        public FieldInfo GetFieldInfo(string name)
        {
            GetFieldInfos();
            for (int i = 0; i < _fieldInfoCache.Length; i++)
            {
                if (_fieldInfoCache[i].Name == name)
                    return _fieldInfoCache[i];
            }
            return null;
        }

        public MethodInfo GetMethodInfo(string name)
        {
            GetMethodInfos();
            for (int i = 0; i < _methodInfoCache.Length; i++)
            {
                if (_methodInfoCache[i].Name == name)
                    return _methodInfoCache[i];
            }
            return null;
        }

        public TAttribute GetCustomAttribute<TAttribute>() where TAttribute : Attribute
        {
            GetAttributes();
            for (int i = 0; i < _attributeCache.Length; i++)
            {
                object att = _attributeCache[i];
                var value = att as TAttribute;
                if (null != value)
                    return value;
            }
            return null;
        } 
    }

    /// <summary>
    /// the TypeData only contains public method,property,field SlidingExpirationCache
    /// </summary>
    internal static class TypeDataCache
    {
        private static readonly ICache<Type, TypeData> Cache = new ReaderWriterCache<Type, TypeData>(MemberInfoComparer<Type>.Default );

        public static TypeData GetTypeData(Type type)
        {
            return Cache.FetchOrCreateItem(type, p => new TypeData(p));
        }

        public static TypeData GetTypeData<T>(T t = default(T))
        {
            return TypeData<T>.Data;
        }

        #region Nested type: TypeData

        private static class TypeData<T>
        {
            public static readonly TypeData Data = new TypeData(typeof (T));
        }

        #endregion
    }
}